package org.jaronsource.framework.core.dao.support;

import static org.jaronsource.framework.core.dao.support.DaoUtils.processFetch;
import static org.jaronsource.framework.core.dao.support.DaoUtils.processFetchCount;
import static org.jaronsource.framework.core.dao.support.DaoUtils.processSort;
import static org.jaronsource.framework.core.dao.support.DaoUtils.processWhere;
import static org.jaronsource.framework.core.dao.support.DaoUtils.setQueryParam;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Transient;
import javax.persistence.TypedQuery;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jaronsource.framework.util.BeanUtils;
import org.springframework.stereotype.Repository;

import com.alibaba.druid.util.StringUtils;

@Repository
public class CommonDao implements ICommonDao {

	protected Log log = LogFactory.getLog( getClass() );

	@PersistenceContext
	protected EntityManager entityManager;

	public void setEntityManager( EntityManager entityManager ) {
		this.entityManager = entityManager;
	}

	public EntityManager getEntityManager() {
		return entityManager;
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <X> List<X> query( String jpql, Map<String, ?> params ) {
		Query query = entityManager.createQuery( jpql.toString() );
		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <X> List<X> query( String jpql, Object... params ) {
		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <X> X queryOne( String jpql, Object... params ) {
		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		query.setMaxResults( 1 );
		List<X> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <X> X queryOne( String jpql, Map<String, ?> params ) {
		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		query.setMaxResults( 1 );
		List<X> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <X> Page<X> queryPage(	PageRequest pageRequest,
									String jpql,
									Object... params ) {
		Page<X> result = null;
		long count = count( jpql, params );

		if ( count > 0 ) {
			List<X> content = query( pageRequest, jpql, params );
			result = new Page<X>( content, pageRequest, count );
		}
		else {
			result = new Page<X>( new ArrayList<X>(), pageRequest, count );
		}

		return result;
	}

	@Override
	public <X> Page<X> queryPage(	PageRequest pageRequest,
									String jpql,
									Map<String, ?> params ) {
		Page<X> result = null;
		long count = count( jpql, params );

		if ( count > 0 ) {
			List<X> content = query( pageRequest, jpql, params );
			result = new Page<X>( content, pageRequest, count );
		}
		else {
			result = new Page<X>( new ArrayList<X>(), pageRequest, count );
		}

		return result;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T save( T target ) {
		if ( target.isNew() ) {
			entityManager.persist( target );
		}
		else {
			entityManager.merge( target );
		}
		return target;
	}

	public void flush() {
		entityManager.flush();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T[]
			save( T... targets ) {
		for ( T target : targets ) {
			save( target );
		}
		return targets;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> void
			update( T target, I pk ) {
		Class<?> clazz = target.getClass();
		String className = clazz.getName();

		StringBuilder jpqlBuilder = new StringBuilder();
		List<Object> params = new ArrayList<Object>();

		jpqlBuilder.append( String.format( "update %s o set", className ) );

		Entity entityAnnotation = clazz.getAnnotation( Entity.class );
		if ( entityAnnotation == null ) throw new RuntimeException( "Entity Annotation not found" );

		String idFieldName = getIdFieldName( clazz );

		int index = 0;
		Map<String, Field> fieldMap = BeanUtils.getAllFieldMap( clazz );
		for ( Iterator<Map.Entry<String, Field>> it = fieldMap.entrySet().iterator(); it.hasNext(); ) {
			Map.Entry<String, Field> entry = it.next();
			Field field = entry.getValue();
			String fieldName = field.getName();
			Transient transientAnnotation = field.getAnnotation( Transient.class );

			if ( transientAnnotation == null && !StringUtils.equals( "serialVersionUID", fieldName ) ) {
				field.setAccessible( true );
				Object fieldValue = null;
				try {
					fieldValue = field.get( target );
				}
				catch ( Exception e ) {}
				if ( fieldValue != null ) {
					if ( index != 0 ) jpqlBuilder.append( "," );
					jpqlBuilder.append( String.format( " o.%s = ?", fieldName ) );
					params.add( fieldValue );
					index++;
				}

			}
		}

		jpqlBuilder.append( String.format( " where o.%s = ?", idFieldName ) );
		params.add( pk );

		if ( index > 0 ) ;
		execute( jpqlBuilder.toString(), params.toArray() );

	}

	private String getIdFieldName( Class<?> clazz ) {
		String result = null;
		Map<String, Field> fieldMap = BeanUtils.getAllFieldMap( clazz );
		for ( Iterator<Map.Entry<String, Field>> it = fieldMap.entrySet().iterator(); it.hasNext(); ) {
			Map.Entry<String, Field> entry = it.next();
			Field field = entry.getValue();
			Id idAnnotation = field.getAnnotation( Id.class );

			if ( idAnnotation != null ) {
				result = field.getName();
				break;

			}
		}
		return result;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> void
			remove( T target ) {
		entityManager.remove( target );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> void
			remove( T... targets ) {
		for ( T target : targets ) {
			remove( target );
		}
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> void
			remove( Class<T> clazz, I id ) {
		remove( findByPk( clazz, id ) );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> void
			remove( Class<T> clazz, I... ids ) {
		for ( I id : ids ) {
			remove( findByPk( clazz, id ) );
		}
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> boolean
			exists( Class<T> clazz, I id ) {
		return findByPk( clazz, id ) != null;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findByPk( Class<T> clazz, I id ) {
		return (T) entityManager.find( clazz, id );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findReferenceByPk( Class<T> clazz, I id ) {
		return (T) entityManager.getReference( clazz, id );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne( Class<T> clazz, Criteria criteria ) {
		Map<String, Object> parameterMap = new HashMap<String, Object>();
		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetch( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT o FROM %s o", clazz.getSimpleName() ) );
		jpqlDataBuilder.append( whereSqlBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, parameterMap );
		query.setMaxResults( 1 );
		List<T> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne( Class<T> clazz, String jpql, Object... params ) {

		TypedQuery<T> query = entityManager.createQuery( jpql, clazz );
		setQueryParam( query, params );
		query.setMaxResults( 1 );

		List<T> tmpList = query.getResultList();

		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne( Class<T> clazz, String jpql, Map<String, ?> params ) {

		TypedQuery<T> query = entityManager.createQuery( jpql, clazz );
		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}
		query.setMaxResults( 1 );

		List<T> tmpList = query.getResultList();

		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			findAll( Class<T> clazz ) {
		return find( clazz, (Sort) null, new Criteria() );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, Criteria criteria ) {

		Map<String, Object> parameterMap = new HashMap<String, Object>();
		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetch( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT o FROM %s o", clazz.getSimpleName() ) );
		jpqlDataBuilder.append( whereSqlBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, parameterMap );

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, String jpql, Object... params ) {

		TypedQuery<T> query = entityManager.createQuery( jpql, clazz );
		setQueryParam( query, params );

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, String jpql, Map<String, ?> params ) {

		TypedQuery<T> query = entityManager.createQuery( jpql, clazz );
		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}
		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> long
			count( Class<T> clazz, Criteria criteria ) {

		Map<String, Object> parameterMap = new HashMap<String, Object>();

		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetchCount( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT count(o) FROM %s o", clazz.getSimpleName() ) );

		jpqlDataBuilder.append( whereSqlBuilder );

		Query query = entityManager.createQuery( jpqlDataBuilder.toString() );

		setQueryParam( query, parameterMap );

		return (Long) query.getSingleResult();
	}

	@Override
	public long count( String jpql, Object... params ) {

		StringBuilder countBuilder = new StringBuilder( "SELECT COUNT(o) FROM " );
		String tm = jpql.substring( jpql.toLowerCase().indexOf( "from " ) + 5 );
		tm = tm.replaceAll( "(?i) fetch", "" );
		countBuilder.append( tm );

		TypedQuery<Long> query = entityManager.createQuery( countBuilder.toString(), Long.class );
		setQueryParam( query, params );
		boolean hasGroupBy = jpql.toLowerCase().indexOf( "group by" ) > -1;
		if ( hasGroupBy ) return query.getResultList().size();
		else return query.getSingleResult();
	}

	@Override
	public long count( String jpql, Map<String, ?> params ) {
		StringBuilder countBuilder = new StringBuilder( "SELECT COUNT(o) FROM " );
		String tm = jpql.substring( jpql.toLowerCase().indexOf( "from " ) + 5 );
		tm = tm.replaceAll( "(?i) fetch", "" );
		countBuilder.append( tm );

		TypedQuery<Long> query = entityManager.createQuery( countBuilder.toString(), Long.class );
		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}
		boolean hasGroupBy = jpql.toLowerCase().indexOf( "group by" ) > -1;
		if ( hasGroupBy ) return query.getResultList().size();
		else return query.getSingleResult();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, Sort sort, Criteria criteria ) {

		Map<String, Object> parameterMap = new HashMap<String, Object>();

		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetch( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT o FROM %s o", clazz.getSimpleName() ) );

		jpqlDataBuilder.append( whereSqlBuilder );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );

		setQueryParam( query, parameterMap );

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, PageRequest pageRequest, Criteria criteria ) {

		Sort sort = pageRequest.getSort();

		Map<String, Object> parameterMap = new HashMap<String, Object>();
		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetch( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT o FROM %s o", clazz.getSimpleName() ) );

		jpqlDataBuilder.append( whereSqlBuilder );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );

		setQueryParam( query, parameterMap );
		query.setFirstResult( pageRequest.getOffset() );
		query.setMaxResults( pageRequest.getPageSize() );

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find( Class<T> clazz, Sort sort, String jpql, Object... params ) {

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, params );

		return query.getResultList();
	}

	@Override
	public
			<T extends IEntity<I>, I extends Serializable>
			List<T>
			find( Class<T> clazz, Sort sort, String jpql, Map<String, ?> params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find(	Class<T> clazz,
					PageRequest pageRequest,
					String jpql,
					Object... params ) {

		Sort sort = pageRequest.getSort();

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, params );

		query.setFirstResult( pageRequest.getOffset() );
		query.setMaxResults( pageRequest.getPageSize() );

		return query.getResultList();
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> List<T>
			find(	Class<T> clazz,
					PageRequest pageRequest,
					String jpql,
					Map<String, ?> params ) {

		Sort sort = pageRequest.getSort();
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}

		query.setFirstResult( pageRequest.getOffset() );
		query.setMaxResults( pageRequest.getPageSize() );

		return query.getResultList();
	}

	@Override
	public
			<T extends IEntity<I>, I extends Serializable>
			Page<T>
			findPage( Class<T> clazz, PageRequest pageRequest, Criteria criteria ) {
		Page<T> result = null;
		long count = count( clazz, criteria );

		if ( count > 0 ) {
			List<T> content = find( clazz, pageRequest, criteria );
			result = new Page<T>( content, pageRequest, count );
		}
		else {
			result = new Page<T>( new ArrayList<T>(), pageRequest, count );
		}

		return result;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> Page<T>
			findPage(	Class<T> clazz,
						PageRequest pageRequest,
						String jpql,
						Object... params ) {
		Page<T> result = null;
		long count = count( jpql, params );

		if ( count > 0 ) {
			List<T> content = find( clazz, pageRequest, jpql, params );
			result = new Page<T>( content, pageRequest, count );
		}
		else {
			result = new Page<T>( new ArrayList<T>(), pageRequest, count );
		}

		return result;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> Page<T>
			findPage(	Class<T> clazz,
						PageRequest pageRequest,
						String jpql,
						Map<String, ?> params ) {
		Page<T> result = null;
		long count = count( jpql, params );

		if ( count > 0 ) {
			List<T> content = find( clazz, pageRequest, jpql, params );
			result = new Page<T>( content, pageRequest, count );
		}
		else {
			result = new Page<T>( new ArrayList<T>(), pageRequest, count );
		}

		return result;
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne( Class<T> clazz, Sort sort, Criteria criteria ) {

		Map<String, Object> parameterMap = new HashMap<String, Object>();

		StringBuilder whereSqlBuilder = new StringBuilder();

		processFetch( criteria, whereSqlBuilder );
		processWhere( criteria, whereSqlBuilder, parameterMap );

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder();
		jpqlDataBuilder.append( String.format( "SELECT o FROM %s o", clazz.getSimpleName() ) );

		jpqlDataBuilder.append( whereSqlBuilder );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		query.setMaxResults( 1 );
		setQueryParam( query, parameterMap );
		List<T> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne( Class<T> clazz, Sort sort, String jpql, Object... params ) {

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, params );

		query.setMaxResults( 1 );
		List<T> tmpList = query.getResultList();

		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@Override
	public <T extends IEntity<I>, I extends Serializable> T
			findOne(	Class<T> clazz,
						Sort sort,
						String jpql,
						Map<String, ?> params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		TypedQuery<T> query = entityManager.createQuery( jpqlDataBuilder.toString(), clazz );
		setQueryParam( query, params );

		query.setMaxResults( 1 );
		List<T> tmpList = query.getResultList();

		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> List<T> query( Sort sort, String jpql, Object... params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> List<T> query( Sort sort, String jpql, Map<String, ?> params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> List<T> query(	PageRequest pageRequest,
								String jpql,
								Object... params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( pageRequest.getSort(), sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		query.setFirstResult( pageRequest.getOffset() );
		query.setMaxResults( pageRequest.getPageSize() );

		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> List<T> query(	PageRequest pageRequest,
								String jpql,
								Map<String, ?> params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( pageRequest.getSort(), sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		query.setFirstResult( pageRequest.getOffset() );
		query.setMaxResults( pageRequest.getPageSize() );

		setQueryParam( query, params );
		return query.getResultList();
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> T queryOne( Sort sort, String jpql, Object... params ) {

		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		query.setMaxResults( 1 );
		List<T> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	@SuppressWarnings ( "unchecked")
	@Override
	public <T> T queryOne( Sort sort, String jpql, Map<String, ?> params ) {
		StringBuilder sortBuilder = new StringBuilder();
		processSort( sort, sortBuilder );

		StringBuilder jpqlDataBuilder = new StringBuilder( jpql );
		jpqlDataBuilder.append( sortBuilder );

		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		query.setMaxResults( 1 );
		List<T> tmpList = query.getResultList();
		return tmpList.isEmpty() ? null : tmpList.get( 0 );
	}

	public int execute( String jpql, Object... params ) {
		Query query = entityManager.createQuery( jpql );
		setQueryParam( query, params );
		return query.executeUpdate();
	}

	public int execute( String jpql, Map<String, ?> params ) {
		Query query = entityManager.createQuery( jpql.toString() );
		setQueryParam( query, params );
		return query.executeUpdate();
	}
}